local Map = require('src.gen.map')
local Screen = require('src.gen.screen')
local Tile = require('src.gen.tile')
local Actor = require('src.gen.actor')
local Utype = require('src.gen.utype')
local Player = require('src.gen.player')
local Unit = require('src.gen.unit')
local UPool = require('src.gen.upool')
local Symbol = require('src.gen.symbol')
local CPU_AI = require "AI.default"

local lume = require('lib.lume')

return Class{
   _name = "Game",
   
   init = function (self)
      self.gui = require('src.gen.gui')()
      self.config = pl.config.read("config")
      self.input = require('src.gen.input')(require "bindings", "default")
      self.current_screen = Screen(512, 512)
   end,
   
   load = function (self, state, map_to_load)
      local state = state or "mainmenu"
      self.symbols_image = love.graphics.newImage("assets/images/ts.png")
      self.units_types = self:load_units ()
      self.cursor = require('src.gen.cursor')(8)
      local splash = require('src.gen.splash')()
      self.states = self:load_states()
      
      self.player = Player()
      self.upool = UPool()
      if state == "deployment" then
	 if not map_to_load then
	    state = "map_view"
	 else
	    self:load_map(map_to_load)
	    state = "deployment"
	 end
      end
      self:switch_state(state or "mainmenu")
   end,
   
   load_states = function (self)
      local l = lume(love.filesystem.getDirectoryItems("states/gen")):map(
	 function (s)
	    local name = pl.path.splitext(s)
	    return {name, require ("states.gen."..name)()}
							     end)
      return l:result()
   end,
   
   get_state = function (self, name)
      if not name then return self.state end
      local s = lume.match(self.states, function (v) return v[1] == name end)
      return s[2]
   end,

   new_game = function (self, mapname)
      self:load_map(mapname)
      self:switch_state("deployment")
   end,
   
   switch_state = function (self, name, no_load)
      if not name then return end
      self.state = self:get_state (name)
      if not no_load then self.state:load() end
      self.state:resize(love.graphics.getDimensions())
      if self.input:has_mode (name) then
	 self.input:switch_mode (name)
      else
	 self.input:switch_mode ("default")
      end
      collectgarbage()
   end,
   
   select = function (self)
      if self.cursor.is_hovering then return end
      local x, y = self.cursor:get_position ()
      if Tile(x, y):select() then
	 
      end
   end,
   
   draw = function (self)
      lume.each({self.state}, function (v) if v.draw then v:draw() end end)
   end,
   
   update = function (self, dt)
      lume.each({
	    self.input, 
	    self.current_screen, 
	    self.cursor,
	    self.state
	     }, function (v) if v.update then v:update(dt) end end)
   end,
   
   get_selected = function (self)
      local x, y = self.cursor.grid:get_highlighted_tile_pos ()
      Game.current_map:marker_get (x, y)
   end,
   
   make_map = function (self, _name, w, h)
      assert(_name and w and h, "make_map: bad input")
      local m = Map(name, w, h)
      return m
   end,
   
   get_actor_selected = function (self)
      return self:actor_on_position (self.current_tile:get_position())
   end,
   
   load_units = function (self)
      return lume(love.filesystem.getDirectoryItems("src/units"))
	 :map(function (ut)
	       local n = pl.path.splitext(ut)
	       return { n, Utype(n) }
	     end):result()
   end,
   
   get_utype = function (self, utypename)
      local r = lume(self.units_types)
	 :match(function (ut)
	       return ut[1] == utypename 
	       end):result()
      if r then return r[2] end
   end,
   
   get_unit = function (self, uname)
      for _, ut in ipairs(self.units_types) do
	 for _, u in ipairs(ut[2].units) do
	    local __u = lume(lume.slice(u, 2))
	       :match(function (unit)
		     return unit[1] == uname
		     end):result()
	    if __u then return __u, ut[1] end
	 end
      end
      return
   end,

   get_unit_symbol = function (self, uname)
      local _u = self:get_unit(uname)
      if _u then
	 return self:get_utype(uname).symbol
      end
   end,
   
   actor_on_position = function (self, x, y)
      for k, v in ipairs(self.actors) do
	 if v.x == x and v.y == y then return v end
      end
   end,
   
   actor_selected = function (self, x, y)
      return self:actor_on_position (self.current_tile:get_position())
   end,
   
   get_actor_info = function (self, name)
      return self:get_actor(name)
   end,
   
   load_map = function (self, name)
      local path = "assets/maps/"..name..".map"
      local content, err = _lf.read(path)
      if not content then return err end
      local mdata = pl.pretty.read(content)
      if not mdata then error("bad map format? couldn't read data") end
      local map = self:make_map(mdata.name, mdata.w, mdata.h)
      lume.each(mdata.markers, function (m)
		   map:marker_add(m.name, m.x, m.y)
      end)
      lume.each(mdata.layers, function (v) map.layers:add_from_str (v.name, v.z, v.w, v.h, v.str) end)
      self.current_map = map
      return map
   end,
   
   load_map_image = function (self, name)
      local map = self:load_map(name)
      local layer = map.layers.tbl[1]
      return layer:get_idata()
   end,
   
   add_random_enemies = function (self, _n, x, y, w, h)
      local n = _n or 5
      local limits_x, limits_h = w, h
      local limx, limy = x or 0, y or 0
      if not limits_x or limits_y then
	 limits_x, limits_y = self.current_map:get_dimensions ()
      end
      for c = 1, n do
	 local x, y = self.cursor.grid:snap(_lmath.random(limx, limits_x), _lmath.random(limy, limits_h))
	 local choice_tbl = {}
	 for k, v in ipairs(self.units_available) do
	    choice_tbl[v[1]] = v[3]
	 end
	 local choice = lume.weightedchoice(choice_tbl)
	 self:add_actor(x, y, choice)
      end
   end,
   
   show_menu = function (self)
      self:switch_state ("mainmenu")
   end,
}
